package com.xin.di.uav.applet.utils;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.URLEncoder;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

@Slf4j
public class ToolUtil {
    private static final String CHARSET = "UTF-8";

    private static final char[] BASE_CHAR = new char[]{'H', 'V', 'E', '8', 'S', '2', 'D', 'Z', 'X', '9', 'C', '7', 'P',
            '5', 'I', 'K', '3', 'M', 'J', '1', 'U', 'F', 'R', '4', 'W', 'Y', '0', 'L', 'T', 'N', '6', 'B', 'G', 'Q'};

    private static final char[] RANDOM_CHAR = new char[]{'0','1','2','3','4','5','6','7','8','9',
            'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
            'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};


    private static final String CHARACTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

    /**
     * api最大版本号
     */
    public static final Integer MAX_API_VERSION = 2147483647;


    public static String generateUID() {
        UUID uuid = UUID.randomUUID();
        long mostSigBits = uuid.getMostSignificantBits();
        long leastSigBits = uuid.getLeastSignificantBits();
        StringBuilder codeBuilder = new StringBuilder();

        for (int i = 0; i < 8; i++) {
            int index = (i % 2 == 0) ? (int) (mostSigBits >>> (i * 9)) : (int) (leastSigBits >>> ((i - 1) * 9));
            index = Math.abs(index);
            codeBuilder.append(CHARACTERS.charAt(index % CHARACTERS.length()));
        }

        return codeBuilder.toString();
    }


    public static int randomInt(int min, int max){
        int n = new Random().nextInt(max - min + 1) + min;
        return n;
    }

    public static Set<Integer> randomInt(int min, int max, int count){
        Set<Integer> set = new HashSet<>();
        int len = max-min+1;

        List<Integer> sources = new ArrayList<>();
        for (int i = 0; i < len; i++) {
            sources.add(min + i);
        }
        if(count > len){
            set = new HashSet<>(sources);
            return set;
        }

        for (int i = 0; i < count; i++) {
            int index = new Random().nextInt(sources.size());
            set.add(sources.get(index));
            sources.remove(index);
        }
        return set;
    }

    public static String randomStr(int length) {
        return randomStr(length, false);
    }
    public static String randomStr(int length, boolean onlyNum) {
        int len = RANDOM_CHAR.length;
        if(onlyNum){
            len = 10;
        }

        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < length; i++) {
            char n = RANDOM_CHAR[(int) Math.round(Math.random() * (len - 1))];
            sb.append(n);
        }
        return sb.toString();
    }

    public static Long strToLong(String str){
        Long n = 0L;
        try {
            n = Long.parseLong(str);
        }catch (Exception e){
            n = 0L;
        }

        return n;
    }

    public static int strToInt(String str){
        Integer n = 0;
        try {
            n = Integer.parseInt(str);
        }catch (Exception e){
            n = 0;
        }

        return n;
    }

    public static boolean isInt(String cs) {
        if (isEmpty(cs)) {
            return false;
        } else {
            try {
                Long.parseLong(cs);
                return true;
            }catch (NumberFormatException e){
                return false;
            }
        }
    }

    public static double strToDouble(String str){
        double n = 0.0;
        try {
            n = Double.parseDouble(str);
        }catch (Exception e){}

        return n;
    }



    public static boolean isEmpty(String str){
        if(str == null || str.trim().length() == 0){
            return true;
        }
        return false;
    }
    public static boolean isEmpty(Collection<?> list){
        if(list == null || list.isEmpty()){
            return true;
        }
        return false;
    }

    public static String completeReplace(String str,String old,String newWord){
        Pattern p = Pattern.compile(old + "([^_//d//w])");
        Matcher m2 = p.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (m2.find()) {
            m2.appendReplacement(sb, newWord+m2.group(1));
        }
        m2.appendTail(sb);
        return sb.toString();
    }

    public static boolean matchComplete(String str,String word){
        Pattern p = Pattern.compile("\\b" + word + "\\b");
        Matcher m2 = p.matcher(str);
        while (m2.find()){
            return true;
        }
        return false;
    }

    public static String getRandomItem(List<String> list, int i){
        if(!isEmpty(list)){
            int size = list.size();
            if(i >= 0 && i< size){
                return list.get(i);
            }else{
                return list.get(new Random().nextInt(size));
            }
        }
        return null;
    }

    public static String strJoin(List<String> list, String delimiter){
        StringJoiner joiner = new StringJoiner(delimiter);
        if(!isEmpty(list)){
            list.forEach(str->{
                joiner.add(str);
            });
        }
        return joiner.toString();
    }

    public static String substringBeforeLast(String str, String separator) {
        if (!isEmpty(str) && !isEmpty(separator)) {
            int pos = str.lastIndexOf(separator);
            return pos == -1 ? str : str.substring(0, pos);
        } else {
            return str;
        }
    }

    public static String substringBefore(String str, String separator) {
        if (!isEmpty(str) && !isEmpty(separator)) {
            int pos = str.indexOf(separator);
            return pos == -1 ? str : str.substring(0, pos);
        } else {
            return str;
        }
    }

    public static String substringAfter(String str, String separator) {
        if (!isEmpty(str) && !isEmpty(separator)) {
            int pos = str.indexOf(separator);
            return pos == -1 ? "" : str.substring(pos + separator.length());
        } else {
            return str;
        }
    }

    public static String substringAfterLast(String str, String separator) {
        if (isEmpty(str)) {
            return str;
        } else if (isEmpty(separator)) {
            return "";
        } else {
            int pos = str.lastIndexOf(separator);
            return pos != -1 && pos != str.length() - separator.length() ? str.substring(pos + separator.length()) : "";
        }
    }

    public static String substringBetween(String str, String open, String close) {
        if (str != null && open != null && close != null) {
            int start = str.indexOf(open);
            if (start != -1) {
                int end = str.indexOf(close, start + open.length());
                if (end != -1) {
                    return str.substring(start + open.length(), end);
                }
            }

            return null;
        } else {
            return null;
        }
    }

    public static String substringLastBetween(String str, String open, String close) {
        if (str != null && open != null && close != null) {
            int start = str.lastIndexOf(open);
            if (start != -1) {
                int end = str.indexOf(close, start + open.length());
                if (end != -1) {
                    return str.substring(start + open.length(), end);
                }
            }

            return null;
        } else {
            return null;
        }
    }

    public static List<String> extractVar(String str, String open, String close){
        String[] strArr = StringUtils.substringsBetween(str, open, close);
        List<String> ids = new ArrayList<>();
        if(strArr != null && strArr.length > 0) {
            for (String s : strArr) {
                ids.add(s);
            }
        }
        return ids;
    }

    public static String[] substringsBetween(String str, String open, String close) {
        if (str != null && !isEmpty(open) && !isEmpty(close)) {
            int strLen = str.length();
            if (strLen == 0) {
                return new String[0];
            } else {
                int closeLen = close.length();
                int openLen = open.length();
                List<String> list = new ArrayList();

                int end;
                for(int pos = 0; pos < strLen - closeLen; pos = end + closeLen) {
                    int start = str.indexOf(open, pos);
                    if (start < 0) {
                        break;
                    }

                    start += openLen;
                    end = str.indexOf(close, start);
                    if (end < 0) {
                        break;
                    }

                    list.add(str.substring(start, end));
                }

                return list.isEmpty() ? null : (String[])list.toArray(new String[list.size()]);
            }
        } else {
            return null;
        }
    }

    public static String replaceVariable(String content, JSONObject data){
        if(data == null){
            data = new JSONObject();
        }
        String pattern = "\\$\\{(.+?)\\}";
        Pattern p = Pattern.compile(pattern);
        Matcher m = p.matcher(content);
        StringBuffer sb = new StringBuffer();
        while (m.find())
        {
            String key = m.group(1);
            String value = data.getString(key);
            m.appendReplacement(sb, value == null ? "0" : value);
        }
        m.appendTail(sb);

        return sb.toString();
    }

    public static String replaceVariable(String content, Map<String, String> data, String defVal){
        if(data == null){
            data = new HashMap<>();
        }
        defVal = defVal == null ? "" : defVal;

        String pattern = "\\$\\{(.+?)\\}";
        Pattern p = Pattern.compile(pattern);
        Matcher m = p.matcher(content);
        StringBuffer sb = new StringBuffer();
        while (m.find())
        {
            String key = m.group(1);
            String value = data.get(key);
            m.appendReplacement(sb, value == null ? defVal : value);
        }
        m.appendTail(sb);

        return sb.toString();
    }

    public static List<String> extractVariable(String str){
        String[] strArr = substringsBetween(str, "${", "}");
        Set<String> set = new HashSet<>();
        if(strArr != null && strArr.length > 0) {
            for (String s : strArr) {
                set.add(s);
            }
        }

        return new ArrayList<String>(set);
    }

    public static String genRoundSn(){
        return System.currentTimeMillis() + String.format("%05d", randomInt(0, 99999));
    }

    public static String formatStr(String str){
        if (str == null || str.length() <= 6) {
            return str;
        } else {
            String regex = "(?<=^.{3}).*(?=.*.{3}$)";
            String replacement = "***";
            Pattern pattern = Pattern.compile(regex);
            Matcher matcher = pattern.matcher(str);

            return matcher.replaceAll(replacement);
        }
    }

    public static String camel2Underline(String camel){
        if(isEmpty(camel)){
            return camel;
        }
        camel=String.valueOf(camel.charAt(0)).toUpperCase().concat(camel.substring(1));
        StringBuffer sb=new StringBuffer();
        Pattern pattern=Pattern.compile("[A-Z]([a-z\\d]+)?");
        Matcher matcher=pattern.matcher(camel);
        while(matcher.find()){
            String word=matcher.group();
            sb.append(word.toLowerCase());
            sb.append(matcher.end()==camel.length()?"":"_");
        }
        return sb.toString();
    }


    public static JSONObject convertToJson(String data){
        String[] filed = data.split("\\^");

        JSONObject json = new JSONObject();
        for (String s : filed) {
            String[] item = s.split(";");
            if(item.length<2){
                continue;
            }
            json.put(item[0], item[1]);
        }
        return json;
    }

    public static boolean hasChinese(String str){
        String regex = ".*[\u4e00-\u9fa5]+.*";
        return str.matches(regex);
    }

    public static boolean hasHtmlTag(String str){
        if(isEmpty(str)){
            return false;
        }

        str = str.replaceAll("\\n", "");

//        String regex =  ".*[(<(\"[^\"]*\"|'[^']*'|[^'\">])*>)|(([*_]).+([*_]))| ((\\*\\*|__).+(\\*\\*|__))|(#{1,6}) ]+.*";
        String regex = ".*(<[^>]+>|\\[[^\\]]+\\])+.*";

        return str.matches(regex);
    }

    public static int countStrNum(String str){
        String regex = "\\d";

        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(str);

        int count = 0;
        while (matcher.find()) {
            count++;
        }
        return count;
    }



    private static Object getJsonValueByKeyPath(JSONObject json, String keyPath) {
        // 按"."切分键路径
        String[] keys = keyPath.split("\\.");

        // 逐级遍历JSON对象
        Object current = json;
        for (String key : keys) {
            int index = -1;
            if (key.endsWith("]")) {
                // 数组索引
                index = Integer.parseInt(key.substring(key.indexOf("[") + 1, key.indexOf("]")));
                key = key.substring(0, key.indexOf("["));
            }

            if (current instanceof JSONObject) {
                JSONObject jsonObject = (JSONObject) current;
                if (index >= 0) {
                    // 数组元素
                    JSONArray jsonArray = jsonObject.getJSONArray(key);
                    if(jsonArray == null){
                        current = null;
                    }else if(index<jsonArray.size()){
                        current = jsonArray.get(index);
                    }else{
                        current = null;
                    }
                } else {
                    // 普通对象
                    current = jsonObject.get(key);
                }
            } else if (current instanceof JSONArray) {
                // 数组元素
                JSONArray jsonArray = (JSONArray) current;
                if(jsonArray == null){
                    current = null;
                }else if(index<jsonArray.size()){
                    current = jsonArray.get(index);
                }else{
                    current = null;
                }
            } else {
                // 未知类型，返回 null
                return null;
            }
        }

        return current;
    }

    public static Object getJsonValueByKeyPathAll(JSONObject json, String keyPath) {
        if(keyPath.contains("[*].")){
            JSONArray arr = new JSONArray();
            String key = substringBefore(keyPath, "[*].");
            Object obj = getJsonValueByKeyPath(json, key);
            if(obj != null && obj instanceof JSONArray){
                JSONArray jsonArray = (JSONArray) obj;
                String subKey = substringAfter(keyPath, "[*].");
                for (int i = 0; i < jsonArray.size(); i++) {
                    arr.add(getJsonValueByKeyPath(jsonArray.getJSONObject(i), subKey));
                }
            }

            return arr;
        }else if(keyPath.contains("[n]")){
            String key = substringBefore(keyPath, "[n]");
            Object obj = getJsonValueByKeyPath(json, key);
            if(obj != null && obj instanceof JSONArray){
                JSONArray jsonArray = (JSONArray) obj;
                int n = randomInt(0, jsonArray.size()-1);
                keyPath = keyPath.replace("[n]", "["+n+"]");
                return getJsonValueByKeyPath(json, keyPath);
            }
            return obj;
        }else {
            return getJsonValueByKeyPath(json, keyPath);
        }

    }

    public static Map<String, String> uriToMap(String uri){
        String params = substringAfter(uri, "?");
        String[] paramArr = params.split("&");

        Map<String, String> map = new HashMap<>();
        for (String param : paramArr) {
            String[] kv = param.split("=");
            if(kv.length>1){
                map.put(kv[0], kv[1]);
            }
        }

        return map;
    }


    public static String uriGetParam(String uri, String key){
        return uriToMap(uri).get(key);
    }

    public static String idToCode(Long id, int codeLen) {
        int baseLen = BASE_CHAR.length;
        char[] buf = new char[baseLen];
        int charPos = baseLen;
        // 当id除以数组长度结果大于0，则进行取模操作，并以取模的值作为数组的坐标获得对应的字符
        while (id / baseLen > 0) {
            int index = (int) (id % baseLen);
            buf[--charPos] = BASE_CHAR[index];
            id /= baseLen;
        }
        buf[--charPos] = BASE_CHAR[(int) (id % baseLen)];
        // 将字符数组转化为字符串
        String result = new String(buf, charPos, baseLen - charPos);
        // 长度不足指定长度则随机补全
        int len = result.length();
        if (len < codeLen) {
            StringBuilder sb = new StringBuilder();
            sb.append('A');
            Random random = new Random();
            // 去除SUFFIX_CHAR本身占位之后需要补齐的位数
            for (int i = 0; i < codeLen - len - 1; i++) {
                sb.append(BASE_CHAR[random.nextInt(baseLen)]);
            }
            result += sb.toString();
        }
        return result;
    }



    /**
     * 处理版本号如果不满足8位数，则补充两个0，为空则直接返回
     * @param version
     * @return
     */
    public static Integer handleVersion(Integer version){
        if(version == null){
            return null;
        }
        // 兼容旧逻辑：判断版本号，如果为8位长度，则后补2个0
        String versionStr = String.valueOf(version);

        if (versionStr.length() == 8) {
            // 后补2个0，相当于乘以100
            return version * 100;
        }

        return version;
    }

    public static String getLanIP(){
        try {
            Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
            while (interfaces.hasMoreElements()) {
                NetworkInterface networkInterface = interfaces.nextElement();
                if (networkInterface.isUp() && !networkInterface.getName().contains("docker")) {
                    Enumeration<InetAddress> addresses = networkInterface.getInetAddresses();
                    while (addresses.hasMoreElements()) {
                        InetAddress address = addresses.nextElement();
                        //log.warn("Local_IP:{}", address.getHostAddress());
                        if (!address.isLoopbackAddress() && address.isSiteLocalAddress()) {
                            return address.getHostAddress();
                        }
                    }
                }
            }
        } catch (SocketException e) {
            log.error("getLanIP err:{}", e);
        }

        return null;
    }

    public static Map<String, String> xmlToMap(String strXML) throws Exception {
        try {
            Map<String, String> data = new HashMap<String, String>();
            DocumentBuilder documentBuilder = newDocumentBuilder();
            InputStream stream = new ByteArrayInputStream(strXML.getBytes("UTF-8"));
            org.w3c.dom.Document doc = documentBuilder.parse(stream);
            doc.getDocumentElement().normalize();
            NodeList nodeList = doc.getDocumentElement().getChildNodes();
            for (int idx = 0; idx < nodeList.getLength(); ++idx) {
                Node node = nodeList.item(idx);
                if (node.getNodeType() == Node.ELEMENT_NODE) {
                    org.w3c.dom.Element element = (org.w3c.dom.Element) node;
                    data.put(element.getNodeName(), element.getTextContent());
                }
            }
            try {
                stream.close();
            } catch (Exception ex) {
                // do nothing
            }
            return data;
        } catch (Exception ex) {
            throw ex;
        }

    }

    public static DocumentBuilder newDocumentBuilder() throws ParserConfigurationException {
        DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false);
        documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
        documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
        documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
        documentBuilderFactory.setXIncludeAware(false);
        documentBuilderFactory.setExpandEntityReferences(false);

        return documentBuilderFactory.newDocumentBuilder();
    }

    public static String mapToKeyValue(Map<String, String> params) {
        StringBuffer keyValue = new StringBuffer("");

        if (params != null) {
            try {
                for (String param : params.keySet()) {
                    String value = params.get(param);
                    keyValue.append("&").append(URLEncoder.encode(param, CHARSET))
                            .append("=").append(URLEncoder.encode(value, CHARSET));
                }
            }catch (IOException e){}
        }
        if(keyValue.length() > 1){
            return keyValue.substring(1);
        }
        return "";
    }
    public static Map<String, String> kvToMap(String kv) {
        Map<String, String> dataMap = new HashMap<>();
        if(kv != null && !kv.isEmpty()) {
            String[] strs = kv.split("&");
            for (int i = 0; i < strs.length; i++) {
                String[] str = strs[i].split("=");
                if (str.length > 1) {
                    dataMap.put(str[0], str[1]);
                }
            }
        }
        return dataMap;
    }




    public static boolean hasVar(String str){
        if(isEmpty(str)){
            return false;
        }
        return str.matches(".*\\{\\{[\\da-zA-Z_]+\\}\\}.*");
    }

    public static boolean isImgUrl(String imgUrl){
        String regex = "^https?://[^\\s/$.?#].[^\\s]*\\.(jpg|jpeg|png|gif|bmp|svg|tiff|webp|heic|raw)$";

        // 创建Pattern对象
        Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);

        Matcher matcher = pattern.matcher(imgUrl.toLowerCase());

        return matcher.matches();
    }





    public static String strJoin(String delimiter, String... str){
        StringJoiner join = new StringJoiner(delimiter);
        for (String s : str) {
            join.add(s);
        }
        return join.toString();
    }


    public static List<String> getCurrUserRoleCodes(){
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication != null) {
            return authentication.getAuthorities().stream().map(authority->authority.getAuthority().substring(5)).collect(Collectors.toList());
        }
        return null;
    }


    public static List<List<String>> listGroup(List<String> ids, Integer groupSize){
        List<List<String>> groupList = new ArrayList<>();
        if(ToolUtil.isEmpty(ids) || groupSize == 0){
            return groupList;
        }
        int total = ids.size();
        int pSize = total / groupSize;
        int lSize = total % groupSize;


        if(pSize>0){
            for (int i = 0; i < pSize; i++) {
                groupList.add(ids.subList(i*groupSize, (i+1)*groupSize));
            }
        }
        if(lSize>0){
            groupList.add(ids.subList(pSize*groupSize, ids.size()));
        }

        return groupList;
    }

    public static <T> List<List<T>> listGroup(List<T> ids, Integer groupSize, Class<T> clazz){
        List<List<T>> groupList = new ArrayList<>();
        if(ToolUtil.isEmpty(ids) || groupSize == 0){
            return groupList;
        }
        int total = ids.size();
        int pSize = total / groupSize;
        int lSize = total % groupSize;


        if(pSize>0){
            for (int i = 0; i < pSize; i++) {
                groupList.add(ids.subList(i*groupSize, (i+1)*groupSize));
            }
        }
        if(lSize>0){
            groupList.add(ids.subList(pSize*groupSize, ids.size()));
        }

        return groupList;
    }

    public static boolean isRoomMsg(String msgId){
        return msgId != null && msgId.startsWith("8");
    }

    public static void main(String[] args) {
        String str = "我有有 {{A}} ads {{B}} 要夺村 {{C}} dfsdf {{D}}";
        List<String> variables = extractVariables(str, "\\{\\{", "\\}\\}");
        System.out.println(variables); // 输出：[A, B, C, D]
    }

    public static List<String> extractVariables(String str, String open, String close) {
        List<String> variables = new ArrayList<>();
        // 使用正则表达式匹配双花括号中的内容
        Pattern pattern = Pattern.compile(open + "(.*?)" + close);
        Matcher matcher = pattern.matcher(str);
        while (matcher.find()) {
            // 提取匹配到的组内容并添加到列表中
            variables.add(matcher.group(1));
        }
        return variables;
    }

    public static boolean validateURL(String url){
        String URL_REGEX = "^(https?|ftp)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]";
        Pattern pattern = Pattern.compile(URL_REGEX);
        return url != null && pattern.matcher(url).matches();
    }

}
